﻿using Acl.Dynamic;

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;

namespace Acl.DataCheck;

internal class CheckValidator : ConstraintValidator
{
    private static Tuple<string, string[]> Parse(ConstraintItem constraint)
    {
        var tokens = ParseToken(constraint.Expression);
        var length = tokens.Length;
        if (length == 0) return new Tuple<string, string[]>($"Check 表达式不能为空。", null);
        if (length is not 3 and not 5)
        {
            return new Tuple<string, string[]>($"Check 表达式格式错误。", null);
        }

        if (tokens[1] is not ("=" or "<" or "<=" or ">" or ">="))
            return new Tuple<string, string[]>($"Check 表达式格式错误。", null);

        if (length is 5 && (tokens[3] is not ("=" or "<" or "<=" or ">" or ">=")))
            return new Tuple<string, string[]>($"Check 表达式格式错误。", null);

        return new Tuple<string, string[]>(null, tokens);
    }

    public static string[] Parse(ConstraintItem constraint, StringBuilder log)
    {
        var expression = constraint.Expression;

        var tuple = Parse(constraint);

        if (!string.IsNullOrEmpty(tuple.Item1))
        {
            log.AppendLine(tuple.Item1);
            return default;
        }

        return tuple.Item2;
    }

    public override void Validate(ValidationContext ctx)
    {
        Guard.NotNull(ctx, nameof(ctx));

        if (ctx.Current.Type != ConstraintType.Check)
            throw new NotSupportedException($"{nameof(CheckValidator)} 不支持 '{ctx.Current.Type}' ,出错相关信息:{ctx.Current} ");

        if (!ctx.Current.Enabled)
            return;

        if (!ctx.Tables.Contains(ctx.Current.TableName))
            throw new NotSupportedException($"{ctx.Current.TableName} 表名不存在 ,出错相关信息:{ctx.Current} ");

        var table = ctx.Tables[ctx.Current.TableName];

        //解析表达式
        var tokens = Parse(ctx.Current, ctx.Logger);
        ctx.Current.Passed = tokens != null;

        if (!ctx.Current.Passed) return;

        SetColumnType(table, tokens, out var columnType, out var columnFlags);
        foreach (dynamic row in table)
        {
            Validate(ctx, tokens, columnType, columnFlags, row);
        }
    }

    private static void SetColumnType(DynamicEntitySet table, string[] tokens, out Type columnType, out bool[] columnFlags)
    {
        columnType = default;
        columnFlags = new bool[tokens.Length];
        for (var i = 0; i < tokens.Length; i = i + 2)
        {
            var col = tokens[i];

            if (table.Members.Contains(col))
            {
                if (columnType == null)
                    columnType = table.Members[col].Type;
                columnFlags[i] = true;
            }
        }
    }

    private void Validate(ValidationContext ctx, string[] tokens, Type columnType, bool[] columnFlags, dynamic row)
    {
        for (var i = 0; (i + 2) < tokens.Length; i += 2)
        {
            Validate(ctx, tokens, columnType, columnFlags, row, i);
        }
    }

    private class RowContext(dynamic value, string operation)
    {
        public dynamic Value { get; } = value;
        public string Operation { get; } = operation;
    }

    private void Validate(ValidationContext ctx, string[] tokens, Type columnType, bool[] columnFlags, dynamic row, int i)
    {
        var val1 = Converter.Convert(columnFlags[i] ? row[tokens[i]] : tokens[i], columnType);
        var val2 = Converter.Convert(columnFlags[i + 2] ? row[tokens[i + 2]] : tokens[i + 2], columnType);

        var v = val1 - val2;
        var operation = tokens[i + 1];
        var rowCtx = new RowContext(v, operation);

        switch (rowCtx)
        {
            case { Operation: ("=" or "=="), Value: not 0 }:
                LogError(operation, columnFlags, tokens, ctx, row, i);
                break;

            case { Operation: "<", Value: > 0 }:
                LogError(operation, columnFlags, tokens, ctx, row, i);
                break;

            case { Operation: "<=", Value: > 0 }:
                LogError(operation, columnFlags, tokens, ctx, row, i);
                break;

            case { Operation: ">", Value: <= 0 }:
                LogError(operation, columnFlags, tokens, ctx, row, i);
                break;

            case { Operation: ">=", Value: <= 0 }:
                LogError(operation, columnFlags, tokens, ctx, row, i);
                break;
        }
    }

    private void LogError(string operation, bool[] columnFlags, string[] tokens, ValidationContext ctx, dynamic row, int startIndex = 0)
    {
        if (columnFlags[startIndex])
        {
            if (columnFlags[startIndex + 2])
            {
                ctx.Current.AppendLine($"{ctx.Current.TableName}.{tokens[startIndex]} {operation}  {ctx.Current.TableName}.{tokens[startIndex + 2]}，记录信息:{row}");
            }
            else
            {
                ctx.Current.AppendLine($"{ctx.Current.TableName}.{tokens[startIndex]} {operation}  {tokens[startIndex + 2]}，记录信息:{row}");
            }
        }
        else
        {
            if (columnFlags[startIndex + 2])
            {
                ctx.Current.AppendLine($"{tokens[startIndex]} {operation}  {ctx.Current.TableName}.{tokens[startIndex + 2]}，记录信息:{row}");
            }
            else
            {
                ctx.Current.AppendLine($"{tokens[startIndex]} {operation}  {tokens[startIndex + 2]}，记录信息:{row}");
            }
        }

        ctx.Current.Passed = false;
    }
}